/**
 * <copyright>
 * Copyrigth 2007 Aurelien Moreau
 *
 * This file is part of ProcessShop.
 *
 * ProcessShop is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * ProcessShop is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with ProcessShop; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * </copyright>
 *
 * $Id$
 */
package net.yienyien.process.bpel.generator.dataFlow;


import java.util.HashMap;
import java.util.Map;

import javax.xml.namespace.QName;

import net.yienyien.process.bpd.BPDObject;
import net.yienyien.process.bpd.Task;
import net.yienyien.process.bpd.util.BpdAdapterFactory;
import net.yienyien.process.bpel.generator.BPELGenerationException;
import net.yienyien.process.bpel.generator.Contrib;
import net.yienyien.process.bpel.generator.ContribImpl;
import net.yienyien.process.bpel.generator.Production;
import net.yienyien.process.bpel.generator.impl.ProductionImpl;
import net.yienyien.process.dfm.Container;
import net.yienyien.process.dfm.DataActivity;
import net.yienyien.process.dfm.DataFlow;
import net.yienyien.process.dfm.DataLink;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.xml.type.util.XMLTypeUtil;
import org.eclipse.wst.wsdl.Operation;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.AbstractFactory;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.AbstractPackage;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TActivity;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TAssign;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TCopy;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TFrom;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TInvoke;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TOnMessage;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TReceive;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TReply;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TSequence;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TTo;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TVariable;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.util.ActivitySetter;

public class DataFlowContrib extends ContribImpl implements Contrib {
	
	public static final String KEY="DataFlowContrib";
	private ProductionImpl prod;
	private DataFlow df;
	private ContainerManager containers;
		
	@Override
	public String getContribKey() {
		return KEY;
	}
	
	@Override
	public void prepare(Production data, EObject contribution) {
		prod=(ProductionImpl) data;
		df=(DataFlow) contribution;
	}
	
	@Override
	public void contrib() throws BPELGenerationException {
		containers=new ContainerManager(prod, df);
		
		prod.getProcess().setVariables(AbstractFactory.eINSTANCE.createTVariables());
		
		for(DataActivity da: df.getDataActivities()) {
			createContainers(prod, da);
		}
		
		for(DataLink dl: df.getDataLinks()) {
			createDataLink(prod, dl);
		}
		
	}
	

	private void createContainers(ProductionImpl prod, DataActivity da) {	
		Object o=prod.getTaskMappings().get(da.getObject());
		
		if(AbstractPackage.eINSTANCE.getTInvoke().isInstance(o)) {
			createContainers(prod, da, (TInvoke) o);
		} else if (AbstractPackage.eINSTANCE.getTReceive().isInstance(o)) {
			createContainers(prod, da, (TReceive) o);
		} else if (AbstractPackage.eINSTANCE.getTReply().isInstance(o)) {
			createContainers(prod, da, (TReply) o);
		} else if (AbstractPackage.eINSTANCE.getTOnMessage().isInstance(o)){
			createContainers(prod, da, (TOnMessage) o);
		}
	}
	
	private String getIContName(Production prod, String base) {
		int c=1;
		String nName= base+"ICont";
		
		top: while(true) {
			for(TVariable var:prod.getProcess().getVariables().getVariable()) {
				if(var.getName().equals(nName)) {
					c++;
					nName=base+"ICont"+c;
					continue top;
				}
			}
			return nName;
		}
	}
	
	private String getOContName(Production prod, String base) {
		int c=1;
		String nName= base+"OCont";
		
		top: while(true) {
			for(TVariable var:prod.getProcess().getVariables().getVariable()) {
				if(var.getName().equals(nName)) {
					c++;
					nName=base+"OCont"+c;
					continue top;
				}
			}
			return nName;
		}
	}
	
	private void createContainers(ProductionImpl prod, DataActivity da, TInvoke invoke) {
		Operation op=prod.getOperationBindings().get(invoke);
		
		TVariable inputContainer=null;
		TVariable outputContainer=null;

		if(op.getInput()!=null && op.getInput().getMessage()!=null) {
			QName input=(QName) XMLTypeUtil.createQName(op.getInput().getMessage().getQName().getNamespaceURI(), op.getInput().getMessage().getQName().getLocalPart(), "");
			inputContainer=addContainer(prod, getIContName(prod, op.getName()), input);
			invoke.setInputVariable(inputContainer.getName());
			if(da.getInputContainer()!=null) {
				containers.addContainer(da.getInputContainer(), inputContainer);
			}
		}
		

		if(op.getOutput()!=null && op.getOutput().getMessage()!=null) {
			QName output=(QName) XMLTypeUtil.createQName(op.getOutput().getMessage().getQName().getNamespaceURI(), op.getOutput().getMessage().getQName().getLocalPart(), "");
			outputContainer=addContainer(prod, getOContName(prod, op.getName()), output);
			invoke.setOutputVariable(outputContainer.getName());
			if(da.getOutputContainer()!=null) {
				containers.addContainer(da.getOutputContainer(), outputContainer);
			}
		}
	}
	
	private void createContainers(ProductionImpl prod, DataActivity da, TReceive receive) {
		Operation op=prod.getOperationBindings().get(receive);
		
		TVariable container=null;
		if(op.getInput()!=null && op.getInput().getMessage()!=null) {
			QName input=(QName) XMLTypeUtil.createQName(op.getInput().getMessage().getQName().getNamespaceURI(), op.getInput().getMessage().getQName().getLocalPart(), "");
			container=addContainer(prod, getIContName(prod, op.getName()), input);
			receive.setVariable(container.getName());
			if(da.getOutputContainer()!=null) {
				containers.addContainer(da.getOutputContainer(), container);
			}
		}
	}
	
	private void createContainers(ProductionImpl prod, DataActivity da, TReply reply) {
		Operation op=prod.getOperationBindings().get(reply);
		TVariable container=null;
		if(op.getOutput()!=null && op.getOutput().getMessage()!=null) {
			QName output=(QName) XMLTypeUtil.createQName(op.getOutput().getMessage().getQName().getNamespaceURI(), op.getOutput().getMessage().getQName().getLocalPart(), "");
			container=addContainer(prod, getOContName(prod, op.getName()), output);
			reply.setVariable(container.getName());
			if(da.getInputContainer()!=null) {
				containers.addContainer(da.getInputContainer(), container);
			}
		}
	}
	private void createContainers(ProductionImpl prod, DataActivity da, TOnMessage onMessage) {
		Operation op=prod.getOperationBindings().get(onMessage);
		TVariable container=null;
		
		if(op.getInput()!=null && op.getInput().getMessage()!=null) {
			QName input=(QName) XMLTypeUtil.createQName(op.getInput().getMessage().getQName().getNamespaceURI(), op.getInput().getMessage().getQName().getLocalPart(), "");
			container=addContainer(prod, getIContName(prod, op.getName()), input);
			onMessage.setVariable(container.getName());
			
			if(da.getOutputContainer()!=null) {
				containers.addContainer(da.getOutputContainer(), container);
			}
		}
	}
	
	private void createDataLink(ProductionImpl prod, DataLink dl) {
		Container srcContainer=dl.getInputActivity().getOutputContainer();
		Container tgtContainer=dl.getOutputActivity().getInputContainer();
		
		BPDObject o=dl.getOutputActivity().getObject();
		TActivity a=(TActivity) prod.getTaskMappings().get(o);
		
		TSequence sequence=AbstractFactory.eINSTANCE.createTSequence();
		TAssign assign=AbstractFactory.eINSTANCE.createTAssign();
		
		if(dl.getMaps()==null || dl.getMaps().size()==0) {
			createMap(assign, srcContainer, tgtContainer, null);
		} else for(net.yienyien.process.dfm.Map m: dl.getMaps()) {
			createMap(assign, srcContainer, tgtContainer, m);
		} 

		ActivitySetter.add(sequence.getActivity(), assign);
		ActivitySetter.add(sequence.getActivity(), a);
		
		prod.getTaskMappings().put(o, sequence);
	}
		
	private void createMap(TAssign assign, Container srcC, Container tgtC, net.yienyien.process.dfm.Map map) {
		TCopy copy;
		

		if(map!=null) {
			copy=containers.createCopy(srcC, map.getFrom(), tgtC, map.getTo());
		} else {
			copy=containers.createCopy(srcC, tgtC);
		}
		

		assign.getCopy().add(copy);
	}

	private TVariable addContainer(ProductionImpl production, String name, QName messageType) {
		TVariable var=AbstractFactory.eINSTANCE.createTVariable();
		var.setName(name);
		var.setMessageType(messageType);
		production.getProcess().getVariables().getVariable().add(var);
		return var;
	}

}

